#include "Subsystems/BlockBehaviors/SubsystemIvyBlockBehavior.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemGameInfo.h"
#include "Managers/BlocksManager.h"
#include "Log.h"

#include "Blocks/AirBlock.h"
#include "Blocks/IvyBlock.h"
#include <mutex>
#include "GameSingleton.h"
#include "GameRandom.hpp"


namespace PocketSurvival
{

    void SubsystemIvyBlockBehavior::onPoll(int32_t value, int32_t x, int32_t y, int32_t z)
    {
        if (m_gameInfo->environmentBehaviorMode == EnvironmentBehaviorMode::Static)
        {
            return;
        }
        if (GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < 0.5f && false == IvyBlock::IsGrowthStopCell(x, y, z) && Terrain::ExtractContents(m_terrain->getCellValue(x, y - 1, z)) == 0)
        {
            m_toUpdateLock.lock();
            m_toUpdate[Point3(x, y - 1, z)] = value;
            m_toUpdateLock.unlock();
        }
    }

    void SubsystemIvyBlockBehavior::onNeighborBlockChanged(int32_t x, int32_t y, int32_t z, int32_t neighborX, int32_t neighborY, int32_t neighborZ)
    {
        int32_t face = IvyBlock::GetFace(Terrain::ExtractData(m_terrain->getCellValue(x, y, z)));
        bool flag = false;
        int32_t cellValue = m_terrain->getCellValue(x, y + 1, z);
        if (Terrain::ExtractContents(cellValue) == IvyBlock::Index() && IvyBlock::GetFace(Terrain::ExtractData(cellValue)) == face)
        {
            flag = true;
        }
        if (false == flag)
        {
            Point3 point = CellFace::FaceToPoint3(face);
            int32_t cellValue2 = m_terrain->getCellValue(x + point.posX, y + point.posY, z + point.posZ);
            if (false == GameSingleton::blocksManager->blocks[Terrain::ExtractContents(cellValue2)]->IsCollidable)
            {
                m_terrain->destroyCell(0, x, y, z, 0, true);
            }
        }
    }

    void SubsystemIvyBlockBehavior::update(float dt)
    {
        if (m_timeOfDay->periodicGameTimeEvent(100.0f, 0.0f) == true)
        {
            std::lock_guard<SpinLock> lk(m_toUpdateLock);
            for (const auto &pair : m_toUpdate)
            {
                m_terrain->changeOldCell(pair.first, AirBlock::Index(), pair.second);
            }
            m_toUpdate.clear();
        }
    }

    void SubsystemIvyBlockBehavior::load(const nlohmann::json &json)
    {
        m_timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        m_terrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();
        m_gameInfo = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>();
    }

    static const std::string subsystemName = "IvyBlockBehavior";
    const std::string &SubsystemIvyBlockBehavior::getName() const
    {
        return subsystemName;
    }
    SubUpdateType SubsystemIvyBlockBehavior::getUpdateType()
    {
        return SubUpdateType::MultiThreadUpdate;
    }
}
